/**
 * @file funcsigner.
 * @author iam002
 * @brief 函数签名器
 * @version 0.1
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef __FUNCSIGNER_H__
#define __FUNCSIGNER_H__

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <json/json.h>
#include "logmanager.h"

#define DEFAULT_ARG_SYM "-"
#define DEFAULT_OPT_SYM "*"
#define DEFAULT_VERSION "1.0.0"
#define DEFAULT_EXIT_KEYWORDS {"Return:", "Return", "Output", "Output:", "return:", "return", "output:", "output"}
#define COMMENT_SYM '%'
#define DEFAULT_KIND "required"
#define DEFAULT_TYPE {"numeric"}

/**
 * @brief 日志宏
 * @example 
 *         LOG_INFO("THIS IS" $ 1 $ std::endl);
 *         LOG_INFO("THIS IS" << 1 << ", NEXT\n");
 * 
 BUG-0819LOG 如果只使用一个std::ostringstream来处理，这应该是MATLAB的问题，日志输出非常奇怪；
 FIXME-0819LOG 解决方案 每次打印日志定义一个临时变量 std::ostringstream
 */
// #define $ <<
// #define LOG_INFO(A) do { \
//     std::ostringstream os; \
//     os << A; \
//     send(os, SendType::INFO); \
// }while(0)

// #define LOG_WARN(A) do { \
//     std::ostringstream os; \
//     os << A; \
//     send(os, SendType::WARN); \
// }while(0)

// #define LOG_ERROR(A) do { \
//     std::ostringstream os; \
//     os << A; \
//     send(os, SendType::ERROR); \
// }while(0)
// #define _BEGIN_LOG_INFO_BUFFER { std::ostringstream os;
// #define _END_LOG_INFO_BUFFER  send(os, SendType::INFO); }

class FuncSigner
{
public:
    enum class SendType {
        INFO,
        WARN,
        ERROR,
    };

    enum class DefineType {
        FUNCTION,
        CLASS,
        NONE
    };

    enum class StateType {
        IDLE,
        PARAMS,
        ARGUMENTS,
        OPTIONS,
        EXIT,
    };

    FuncSigner();
    FuncSigner(const std::string & dirPath, bool verbose = true);
    ~FuncSigner();

    void setArgSym(const char* argsym);
    void setOptSym(const char* optsym);
    void setVesion(const char* version);
    void setVerbose(bool verbose);
    void setVersion(const char* version);
    void setDefaultKind(const std::string & str);
    void setDefaultType(const std::vector<std::string> & vec);

    /**
     * @brief 在指定文件夹上生成函数签名json文件
     * 
     * @param dirPath 指定文件夹
     * @param verbose 是否输出过程信息
     * @param argsym  函数输入参数标识，默认为 -
     * @param optsym  函数输出参数标识，默认为 *
     * @param version json版本信息，默认为 1.0.0
     * @param str  参数默认kind类型，默认为 "required"
     * @param vec  参数默认type类型，默认为 {"numeric"}
     * @return true 
     * @return false 
     */
    // bool generate(const std::string & dirPath
    //             , bool verbose = false
    //             , const char* argsym = DEFAULT_ARG_SYM
    //             , const char* optsym = DEFAULT_OPT_SYM
    //             , const char* version = DEFAULT_VERSION
    //             , const std::string & str = DEFAULT_KIND
    //             , const std::vector<std::string> & vec = DEFAULT_EXIT_KEYWORDS
    //             );
    bool generate(const std::string & dirPath);

/* BUG 由于matlab的fprintf中文支持不友好，目前已放弃编译为 mex 格式 */
/* 日志格式改为 easylogging */
// protected:
//     /* 与MATLAB的输出交互由MEX进行覆盖实现 */
//     virtual void displayErrorOnMatlab(const std::string & msg);
//     virtual void displayWarnOnMatlab(const std::string & msg);
//     virtual void displayOnMatlab(const std::string & msg);

//     /* 给m_os添加回车符号后发送消息, 然后重置m_os的状态和内容  */
//     const char* send(const std::ostringstream & os, SendType type = SendType::INFO);

private:
    /* 初始化目录文件路径 */
    void init();

    /* 获取接下来的第一个非空行 */
    bool skipEmptyAndGetLine(std::ifstream & fin, std::string & line);

    /* 判断文件是否为类或函数定义文件 */
    bool isDefineFile(std::ifstream & fin, std::string & def_name, FuncSigner::DefineType & def_type);

    /**
     * @brief 解析文件路径
     * 
     * @param filepath m文件路径
     * @param dir_len  工作目录字符串长度（需要包含到'\'或'/'）
     * @param def_name 解析的定义名
     * @param def_prefix 返回函数签名
     * @return true 
     * @return false 
     */
    bool parsePath(const std::string & filepath, int dir_len, const std::string & def_name, std::string & def_prefix);

    /**
     * @brief 判断是否为类文件@下的文件夹
     * 
     * @param filepath 
     * @return true 
     * @return false 
     */ 
    bool isClassFolder(const std::string & filepath);

    /**
     * @brief 获取当前行第n个单词（空格作为分割符）
     * 
     * @param line 
     * @param n 
     * @param word 
     * @return true 
     * @return false 
     */
    bool getOneWord(const std::string & line, int n, std::string & word);

    /**
     * @brief 返回state字符串
     * 
     * @param state 
     * @return const char* 
     */
    const char * getStateStr(StateType state);

    /**
     * @brief 根据上一次状态，解析当前行的状态
     * 
     * @param line 
     * @param state 
     * @return StateType 
     */
    StateType getStateFrom(const std::string & line, StateType state);

    /**
     * @brief 解析参数行
     * 
     * @param line 
     * @param state 
     * @param argument 
     * @return StateType 
     */
    StateType parseArgument(const std::string & line, StateType state, Json::Value & argument);

    /**
     * @brief 解析选项行
     * 
     * @param line 
     * @param state 
     * @param option 
     * @return StateType 
     */
    StateType parseOption(const std::string & line, StateType state, std::string & option);


    /**
     * @brief 替换为选项字符串
     * 
     * @param arguments 
     * @param options 
     */
    bool replaceChoices(std::vector<Json::Value> & arguments, std::vector<std::string> & options);

    /**
     * @brief 获取选项字符串
     * 
     * @param options 
     * @param str 
     */
    void getChoicesStr(const std::vector<std::string> & options, std::string & str);

    /**
     * @brief 为指定文件添加函数签名
     * 
     * @param filepath 
     * @return true 
     * @return false 
     */
    bool addOneFuncSign(const std::string & filepath);
    
    std::string m_dirPath;          // 工作目录
    Json::Value m_root;             // JSON 根对象
    bool m_verbose;                 // 是否输出中间信息 
    std::string m_argSym;   // 参数项标识, 默认为 '-'
    std::string m_optSym;   // 选项标识，默认为 '*'
    std::string m_version;  // 版本信息, 默认为 '1.0.0'
    std::vector<std::string> m_exitKeywords; // 识别为退出状态的关键字列表
    std::string m_default_kind;
    std::vector<std::string> m_default_type;
};


#endif
